package com.xbongbong.saas.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasMenuEntity;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.help.menu.InitJxcMenuHelp;
import com.xbongbong.paas.log.MongoLogHelp;
import com.xbongbong.paas.log.constant.LogMemoConstant;
import com.xbongbong.paas.model.PaasMenuModel;
import com.xbongbong.paas.pojo.AccordConfigMenuPojo;
import com.xbongbong.paas.pojo.dto.SaasFormSaveDTO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisConstant;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.service.CompanyConfigService;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.BaseDTO;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.enums.AccessLinkEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.menu.enums.AccordConfigMenuEnum;
import com.xbongbong.pro.enums.menu.enums.JXCMenuEnum;
import com.xbongbong.pro.flowbill.dto.FlowBillInsertDTO;
import com.xbongbong.pro.rabbitmq.producer.CostRecalculationProducer;
import com.xbongbong.pro.workflow.enums.WorkflowOperateEnum;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.BatchFlowBillEntity;
import com.xbongbong.saas.domain.entity.BatchFlowBillUpdateEntity;
import com.xbongbong.saas.domain.entity.BatchSurplusRecordEntity;
import com.xbongbong.saas.domain.entity.CostAdjustProductEntity;
import com.xbongbong.saas.domain.entity.InstockProductEntity;
import com.xbongbong.saas.domain.entity.OutstockProductEntity;
import com.xbongbong.saas.domain.entity.WarehouseOristockEntity;
import com.xbongbong.saas.domain.entity.WarehouseOristockProductEntity;
import com.xbongbong.saas.domain.entity.ext.CostAdjustEntityExt;
import com.xbongbong.saas.domain.entity.ext.InstockEntityExt;
import com.xbongbong.saas.domain.entity.ext.OutstockEntityExt;
import com.xbongbong.saas.domain.entity.ext.ProductEntityExt;
import com.xbongbong.saas.domain.entity.ext.WarehouseEntityExt;
import com.xbongbong.saas.enums.InstockTypeEnum;
import com.xbongbong.saas.enums.OperateModuleTypeEnum;
import com.xbongbong.saas.enums.OperateTypeEnum;
import com.xbongbong.saas.enums.OutstockTypeEnum;
import com.xbongbong.saas.enums.StockTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractOutstockEnum;
import com.xbongbong.saas.enums.business.CostAdjustEnum;
import com.xbongbong.saas.enums.business.InstockEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.business.PurchaseInstockEnum;
import com.xbongbong.saas.enums.business.RefundInstockEnum;
import com.xbongbong.saas.enums.business.ReturnedPurchaseOutstockEnum;
import com.xbongbong.saas.enums.business.WorkOrderOutstockEnum;
import com.xbongbong.saas.help.workflow.BatchFlowBillHelp;
import com.xbongbong.saas.model.BatchFlowBillModel;
import com.xbongbong.saas.model.BatchSurplusRecordModel;
import com.xbongbong.saas.model.CostAdjustModel;
import com.xbongbong.saas.model.CostAdjustProductModel;
import com.xbongbong.saas.model.InstockModel;
import com.xbongbong.saas.model.InstockProductModel;
import com.xbongbong.saas.model.OutstockModel;
import com.xbongbong.saas.model.OutstockProductModel;
import com.xbongbong.saas.model.ProductModel;
import com.xbongbong.saas.model.WarehouseModel;
import com.xbongbong.saas.model.WarehouseOristockModel;
import com.xbongbong.saas.model.WarehouseOristockProductModel;
import com.xbongbong.saas.service.BatchFlowBillService;
import com.xbongbong.workflow.domain.entity.WorkflowTaskEntity;
import com.xbongbong.workflow.domain.entity.WorkflowTransferDataEntity;
import com.xbongbong.workflow.model.WorkflowTaskModel;
import com.xbongbong.workflow.model.WorkflowTransferDataModel;
import com.xbongbong.workflow.pojo.WorkflowTransferPOJO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service("batchFlowBillService")
public class BatchFlowBillServiceImpl implements BatchFlowBillService {

    private static final Logger LOG = LoggerFactory.getLogger(BatchFlowBillServiceImpl.class);
    @Resource
    private PaasMenuModel paasMenuModel;
    @Resource
    private MongoLogHelp mongoLogHelp;
    @Resource
    private InitJxcMenuHelp initJxcMenuHelp;
    @Resource
    private BatchFlowBillModel batchFlowBillModel;
    @Resource
    private BatchSurplusRecordModel batchSurplusRecordModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private WarehouseModel warehouseModel;
    @Resource
    private ProductModel productModel;
    @Resource
    private InstockProductModel instockProductModel;
    @Resource
    private InstockModel instockModel;
    @Resource
    private OutstockProductModel outstockProductModel;
    @Resource
    private CostRecalculationProducer costRecalculationProducer;
    @Resource
    private OutstockModel outstockModel;
    @Resource
    private WarehouseOristockModel warehouseOristockModel;
    @Resource
    private WarehouseOristockProductModel warehouseOristockProductModel;
    @Resource
    private CompanyConfigService companyConfigService;
    @Resource
    private BatchFlowBillHelp batchFlowBillHelp;
    @Resource
    private CostAdjustProductModel costAdjustProductModel;
    @Resource
    private CostAdjustModel costAdjustModel;
    @Resource
    private WorkflowTransferDataModel workflowTransferDataModel;
    @Resource
    private WorkflowTaskModel workflowTaskModel;
    @Resource
    private CommonHelp commonHelp;

    @Override
    public PaasMenuEntity addMenu(AccordConfigMenuPojo accordConfigMenuPojo) throws XbbException {
        String corpid = accordConfigMenuPojo.getCorpid();
        PaasMenuEntity inventoryMenu = paasMenuModel.getByAlias(JXCMenuEnum.INVENTORY_MANAGEMENT.getAlias(), corpid);
        if (Objects.isNull(inventoryMenu)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, I18nMessageUtil.getMessage(MessageConstant.MENU_NOT_EXIST));
        }
        long now = DateTimeUtil.getInt();
        Integer maxSort = paasMenuModel.maxSortInSubMenu(corpid, inventoryMenu.getId(), DelEnum.NORMAL.getDel());
        PaasMenuEntity batchFlowMenuEntity = new PaasMenuEntity(corpid, inventoryMenu.getAppId(), AccordConfigMenuEnum.BATCH_FLOW_BILL.getName(), AccordConfigMenuEnum.BATCH_FLOW_BILL.getAlias(), maxSort + 1, AccessLinkEnum.BATCH_FLOW_BILL.getUrl(), AccessLinkEnum.BATCH_FLOW_BILL.getMobileUrl(), 1, AccessLinkEnum.BATCH_FLOW_BILL.getColor(), AccessLinkEnum.BATCH_FLOW_BILL.getIcon(), accordConfigMenuPojo.getUserId());
        batchFlowMenuEntity.setParentId(inventoryMenu.getId());
        //如果经销商标志为空，设置默认值
        if (Objects.isNull(batchFlowMenuEntity.getDistributorMark())){
            batchFlowMenuEntity.setDistributorMark(BasicConstant.ZERO);
        }
        paasMenuModel.insert(batchFlowMenuEntity);
        String memo = String.format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_ADD_MENU), accordConfigMenuPojo.getLoginUserName(), "");
        mongoLogHelp.buildLog(corpid, accordConfigMenuPojo.getUserId(), accordConfigMenuPojo.getLoginUserName(), OperateModuleTypeEnum.PAAS_MENU, OperateTypeEnum.NEW, "", "", memo, accordConfigMenuPojo.getHttpHeader());
        initJxcMenuHelp.generateFormWithMenu(accordConfigMenuPojo, corpid, now, batchFlowMenuEntity, AccordConfigMenuEnum.BATCH_FLOW_BILL);
        return batchFlowMenuEntity;
    }

    @Override
    @Async(value = "flowThreadPool")
    public void batchFlowModification4Save(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        batchFlowBillHelp.batchFlowModification4Save(saasFormSaveDTO.getCorpid(), saasFormSaveDTO.getBusinessType(), saasFormSaveDTO.getNewData(), saasFormSaveDTO.getNewPaasFormDataEntity());
    }

    @Override
    @Async(value = "flowThreadPool")
    public void batchFlowModification4Delete(List<Long> deleteList, List<BatchFlowBillUpdateEntity> batchFlowBillUpdateEntityList, Integer businessType, String corpid) throws XbbException {
        batchFlowBillHelp.batchFlowModification4Delete(deleteList, batchFlowBillUpdateEntityList, businessType, corpid);
    }

    @Override
    @Async(value = "flowThreadPool")
    public void insertMonthBeforeBatchFlow(FlowBillInsertDTO flowBillInsertDTO) throws XbbException{
        Long beforeSeconds = flowBillInsertDTO.getBeforeSeconds();
        if (beforeSeconds == null) {
            beforeSeconds = BusinessConstant.THIRTY_ONE_DAY;
            flowBillInsertDTO.setBeforeSeconds(BusinessConstant.THIRTY_ONE_DAY);
        }
        String corpid = flowBillInsertDTO.getCorpid();
        Long todayInt = DateTimeUtil.getTodayInt();
        Long monthBeforeInt = todayInt - beforeSeconds;
        paasRedisHelper.setValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid, new JSONObject(), RedisConstant.TINY_DURATION);
        try {
            // 插入，往前推流水，后面会更新
            batchSurplusRecordModel.insertRecordsFromBatchStock(corpid, monthBeforeInt);
            Long idGte = 0L;
            Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID,corpid);
            param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
            List<WarehouseEntityExt> warehouseEntityExtList = warehouseModel.findEntitys(param);
            if (warehouseEntityExtList.isEmpty()) {
                paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
                return;
            }
            List<Long> warehouseIdList = new ArrayList<>();
            warehouseEntityExtList.forEach(item -> warehouseIdList.add(item.getId()));
            while (true) {
                param.clear();
                param.put(StringConstant.CORPID,corpid);
                param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
                param.put("dataParentIdGte",0L);
                param.put("idGte",idGte);
                param.put("pageSize",1000);
                param.put("orderByStr","id asc");
                List<ProductEntityExt> productEntityExtList = productModel.findEntitys(param);
                if (productEntityExtList.isEmpty()) {
                    break;
                }
                idGte = productEntityExtList.get(productEntityExtList.size()-1).getId();
                List<Long> productIdList = new ArrayList<>();
                productEntityExtList.forEach(item -> productIdList.add(item.getId()));
                List<BatchFlowBillEntity> flowBillEntityList = new ArrayList<>();
                // 入库
                param.clear();
                param.put(ParameterConstant.CORPID,corpid);
                param.put("productIdIn",productIdList);
                param.put("warehouseIdIn",warehouseIdList);
                param.put("batchIsNotEmpty", 1);
                param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
                List<InstockProductEntity> instockProductEntityList = instockProductModel.findEntitys(param);
                List<Long> idList = new ArrayList<>();
                Map<Long, InstockEntityExt>  instockEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!instockProductEntityList.isEmpty()) {
                    List<Long> instockIdList = new ArrayList<>();
                    instockProductEntityList.forEach(item -> instockIdList.add(item.getIntoWarehouseId()));
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", instockIdList);
                    filterMap.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<InstockEntityExt> instockEntityList = instockModel.findEntitys(filterMap);
                    instockEntityList.forEach(item -> {
                        idList.add(item.getId());
                        instockEntityExtMap.put(item.getId(), item);
                    });
                }
                Map<String,Double>  productBatchStockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<String,Double>  productBatchCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for (InstockProductEntity entity : instockProductEntityList){
                    if (!idList.contains(entity.getIntoWarehouseId())){
                        continue;
                    }
                    // 批次总库存
                    String key = entity.getProductId() + "_" + entity.getWarehouseId()  + "_" + entity.getBatch()  + "_" + entity.getProduceDate()  + "_" + entity.getGuaranteePeriod();
                    Double productBatchStock = productBatchStockMap.getOrDefault(key,0D);
                    productBatchStock = Arith.add(productBatchStock, entity.getProductNum());
                    productBatchStockMap.put(key,productBatchStock);
                    // 批次总成本
                    Double productBatchCost = productBatchCostMap.getOrDefault(key,0D);
                    productBatchCost = Arith.add(productBatchCost, Arith.mul(entity.getProductNum(), entity.getCost()));
                    productBatchCostMap.put(key, productBatchCost);
                    // 流水记录
                    InstockEntityExt instockEntityExt = instockEntityExtMap.get(entity.getIntoWarehouseId());
                    Long dateTime = instockEntityExt.getData().getLong(InstockEnum.TIME.getAttr());
                    Integer type = instockEntityExt.getData().getInteger(InstockEnum.TYPE.getAttr());
                    Integer businessType = InstockTypeEnum.getStockBusinessTypeByCode(type) == 0 ? XbbRefTypeEnum.OTHER_INSTOCK.getCode() : InstockTypeEnum.getStockBusinessTypeByCode(type);
                    Long businessId = null;
                    String businessName = null;
                    if (Objects.equals(businessType, XbbRefTypeEnum.REFUND_INSTOCK.getCode())) {
                        businessId = instockEntityExt.getData().getLong(RefundInstockEnum.CUSTOMER_ID.getAttr());
                        businessName = instockEntityExt.getData().getString(RefundInstockEnum.CUSTOMER_ID_LINKED_TEXT.getAttr());
                    } else if (Objects.equals(businessType, XbbRefTypeEnum.PURCHASE_INSTOCK.getCode())) {
                        businessId = instockEntityExt.getData().getLong(PurchaseInstockEnum.SUPPLIER_ID.getAttr());
                        businessName = instockEntityExt.getData().getString(PurchaseInstockEnum.SUPPLIER_ID_LINKED_TEXT.getAttr());
                    }
                    BatchFlowBillEntity batchFlowBillEntity = new BatchFlowBillEntity(corpid, entity.getParentId(), entity.getProductId(), entity.getWarehouseId(), entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod(), entity.getProduceDate() + entity.getGuaranteePeriod(),
                            businessId, businessName, entity.getProductNum(), entity.getCost() == null ? 0D : entity.getCost(), productBatchStock, productBatchCost, dateTime, businessType, key, entity.getMemo(), entity.getId(), StockTypeEnum.IN_STOCK.getCode()
                            , instockEntityExt.getSerialNo(), entity.getIntoWarehouseId());
                    flowBillEntityList.add(batchFlowBillEntity);
                }
                //出库
                List<OutstockProductEntity> outstockProductEntityList = outstockProductModel.findEntitys(param);
                idList.clear();
                Map<Long, OutstockEntityExt>  outstockEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!outstockProductEntityList.isEmpty()) {
                    List<Long> outstockIdList = new ArrayList<>();
                    outstockProductEntityList.forEach(item -> outstockIdList.add(item.getOutWarehouseId()));
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", outstockIdList);
                    filterMap.put("del", 0);
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<OutstockEntityExt> outstockEntityList = outstockModel.findEntitys(filterMap);
                    outstockEntityList.forEach(item -> {
                        idList.add(item.getId());
                        outstockEntityExtMap.put(item.getId(), item);
                    });
                }
                for (OutstockProductEntity entity : outstockProductEntityList){
                    if (!idList.contains(entity.getOutWarehouseId())){
                        continue;
                    }
                    String key = entity.getProductId() + "_" + entity.getWarehouseId()  + "_" + entity.getBatch()  + "_" + entity.getProduceDate()  + "_" + entity.getGuaranteePeriod();
                    Double productBatchStock = productBatchStockMap.getOrDefault(key,0D);
                    productBatchStock = Arith.sub(productBatchStock, entity.getProductNum());
                    productBatchStockMap.put(key, productBatchStock);
                    // 分仓总成本
                    Double productBatchCost = productBatchCostMap.getOrDefault(key,0D);
                    productBatchCost = Arith.sub(productBatchCost, Arith.mul(entity.getProductNum(), entity.getCost()));
                    productBatchCostMap.put(key, productBatchCost);
                    // 流水记录
                    OutstockEntityExt outstockEntityExt = outstockEntityExtMap.get(entity.getOutWarehouseId());
                    Long dateTime = outstockEntityExt.getData().getLong(OutstockEnum.TIME.getAttr());
                    Integer type = outstockEntityExt.getData().getInteger(OutstockEnum.TYPE.getAttr());
                    Integer businessType = OutstockTypeEnum.getStockBusinessTypeByCode(type) == 0 ? XbbRefTypeEnum.OTHER_OUTSTOCK.getCode() : OutstockTypeEnum.getStockBusinessTypeByCode(type);
                    Long businessId = null;
                    String businessName = null;
                    if (Objects.equals(businessType, XbbRefTypeEnum.CONTRACT_OUTSTOCK.getCode())) {
                        businessId = outstockEntityExt.getData().getLong(ContractOutstockEnum.LINK_CUSTOMER.getAttr());
                        businessName = outstockEntityExt.getData().getString(ContractOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
                    } else if (Objects.equals(businessType, XbbRefTypeEnum.RETURNED_PURCHASE_OUTSTOCK.getCode())) {
                        businessId = outstockEntityExt.getData().getLong(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER.getAttr());
                        businessName = outstockEntityExt.getData().getString(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER_HIDE.getAttr());
                    } else if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER_OUTSTOCK.getCode())) {
                        businessId = outstockEntityExt.getData().getLong(WorkOrderOutstockEnum.LINK_CUSTOMER.getAttr());
                        businessName = outstockEntityExt.getData().getString(WorkOrderOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
                    }
                    BatchFlowBillEntity batchFlowBillEntity = new BatchFlowBillEntity(corpid, entity.getParentId(), entity.getProductId(), entity.getWarehouseId(), entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod(), entity.getProduceDate() + entity.getGuaranteePeriod(),
                            businessId, businessName, entity.getProductNum(), entity.getCost() == null ? 0D : entity.getCost(), productBatchStock, productBatchCost, dateTime, businessType, key, entity.getMemo(), entity.getId(), StockTypeEnum.OUT_STOCK.getCode()
                            , outstockEntityExt.getSerialNo(), entity.getOutWarehouseId());
                    flowBillEntityList.add(batchFlowBillEntity);
                }
                //成本调整单
                //获取成本调整单产品关联表数据
                List<CostAdjustProductEntity> costAdjustProductEntityList = costAdjustProductModel.findEntitys(param);
                idList.clear();
                Map<Long, CostAdjustEntityExt> costAdjustEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!costAdjustProductEntityList.isEmpty()) {
                    List<Long> costAdjustIdList = new ArrayList<>();
                    costAdjustProductEntityList.forEach(item -> costAdjustIdList.add(item.getCostAdjustId()));
                    //获取成本调整单集合
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", costAdjustIdList);
                    filterMap.put("del", 0);
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<CostAdjustEntityExt> costAdjustEntityExtList = costAdjustModel.findEntitys(filterMap);
                    costAdjustEntityExtList.forEach(item -> {
                        idList.add(item.getId());
                        costAdjustEntityExtMap.put(item.getId(), item);
                    });
                    for (CostAdjustProductEntity entity : costAdjustProductEntityList){
                        if (!idList.contains(entity.getCostAdjustId())){
                            continue;
                        }
                        String key = entity.getProductId() + "_" + entity.getWarehouseId()  + "_" + entity.getBatch()  + "_" + entity.getProduceDate()  + "_" + entity.getGuaranteePeriod();
                        Double productBatchStock = productBatchStockMap.getOrDefault(key,0D);
                        productBatchStockMap.put(key, productBatchStock);
                        // 分仓总成本
                        Double productBatchCost = productBatchCostMap.getOrDefault(key,0D);
                        productBatchCost = Arith.add(productBatchCost, entity.getCostAdjustAmount().doubleValue());
                        productBatchCostMap.put(key, productBatchCost);
                        // 流水记录
                        CostAdjustEntityExt costAdjustEntityExt = costAdjustEntityExtMap.get(entity.getCostAdjustId());
                        Long dateTime = costAdjustEntityExt.getData().getLong(CostAdjustEnum.ADJUST_TIME.getAttr());
                        Integer businessType = XbbRefTypeEnum.COST_ADJUST.getCode();
                        BatchFlowBillEntity batchFlowBillEntity = new BatchFlowBillEntity(corpid, entity.getParentId(), entity.getProductId(), entity.getWarehouseId(), entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod(), entity.getProduceDate() + entity.getGuaranteePeriod(),
                                null, null, entity.getProductNum() == null ? 0 : entity.getProductNum(), entity.getCostAdjustAmount().doubleValue(), productBatchStock, productBatchCost, dateTime, businessType, key, entity.getMemo(), entity.getId(), StockTypeEnum.ADJUST_STOCK.getCode()
                                , costAdjustEntityExt.getSerialNo(), entity.getCostAdjustId());
                        flowBillEntityList.add(batchFlowBillEntity);
                    }
                }
                //期初
                param.clear();
                param.put(StringConstant.CORPID,corpid);
                param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
                param.put("warehouseIdIn", warehouseIdList);
                param.put("greaterThanAddTime",monthBeforeInt);
                param.put("batchIsNotEmpty", 1);
                List<WarehouseOristockEntity> warehouseOristockEntityList = warehouseOristockModel.findEntitys(param);
                Map<Long,WarehouseOristockEntity> oriWarehouseMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                warehouseOristockEntityList.forEach(item -> oriWarehouseMap.put(item.getId().longValue(),item));
                param.put("oriStockIdIn",oriWarehouseMap.keySet());
                param.put("productIdIn",productIdList);
                param.put("batchIsNotEmpty", 1);
                List<WarehouseOristockProductEntity> warehouseOristockProductEntityList = warehouseOristockProductModel.findEntitys(param);
                for (WarehouseOristockProductEntity entity : warehouseOristockProductEntityList) {
                    if (oriWarehouseMap.containsKey(entity.getOristockId().longValue())){
                        WarehouseOristockEntity warehouseOristockEntity = oriWarehouseMap.get(entity.getOristockId().longValue());
                        Long warehouseId = warehouseOristockEntity.getWarehouseId();
                        String key = entity.getProductId() + "_" + warehouseId  + "_" + entity.getBatch()  + "_" + entity.getProduceDate()  + "_" + entity.getGuaranteePeriod();
                        Double productBatchStock = productBatchStockMap.getOrDefault(key,0D);
                        productBatchStock = Arith.add(productBatchStock, entity.getOristockNum());
                        productBatchStockMap.put(key, productBatchStock);
                        // 批次总成本
                        Double productBatchCost = productBatchCostMap.getOrDefault(key,0D);
                        productBatchCost = Arith.add(productBatchCost, Arith.mul(entity.getOristockNum(), entity.getCost() == null ? 0D : entity.getCost()));
                        productBatchCostMap.put(key, productBatchCost);
                        // 流水记录
                        Long dateTime = DateTimeUtil.getTodayInt(warehouseOristockEntity.getAddTime());
                        BatchFlowBillEntity batchFlowBillEntity = new BatchFlowBillEntity(corpid, entity.getParentId(), entity.getProductId(), warehouseId, entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod(), entity.getProduceDate() + entity.getGuaranteePeriod(),
                                null, null, entity.getOristockNum(), entity.getCost() == null ? 0D : entity.getCost(), productBatchStock, productBatchCost, dateTime, XbbRefTypeEnum.ORI_STOCK.getCode(), key, entity.getMemo(), entity.getId(), StockTypeEnum.ORI_STOCK.getCode()
                                , warehouseOristockEntity.getSerialNo(), entity.getOristockId().longValue());
                        flowBillEntityList.add(batchFlowBillEntity);
                    }
                }
                List<BatchFlowBillEntity> sortedInsertList = flowBillEntityList.stream().sorted(Comparator.comparing(BatchFlowBillEntity::getDate)).collect(Collectors.toList());
                Map<String, List<BatchFlowBillEntity>> sortedInsertMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                sortedInsertList.forEach(item -> {
                    List<BatchFlowBillEntity> sortedList = sortedInsertMap.getOrDefault(item.getBatchKey(), new ArrayList<>());
                    sortedList.add(item);
                    sortedInsertMap.put(item.getBatchKey(), sortedList);
                });
                if (!productBatchStockMap.keySet().isEmpty()) {
                    param.clear();
                    param.put(StringConstant.CORPID,corpid);
                    param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
                    param.put("date",monthBeforeInt);
                    param.put("batchKeyIn", productBatchStockMap.keySet());
                    List<BatchSurplusRecordEntity> batchSurplusRecordEntityList = batchSurplusRecordModel.findEntitys(param);
                    for (BatchSurplusRecordEntity batchSurplusRecordEntity : batchSurplusRecordEntityList) {
                        String batchKey = batchSurplusRecordEntity.getBatchKey();
                        if (sortedInsertMap.containsKey(batchKey)) {
                            Double remainCost = batchSurplusRecordEntity.getRemainingCost();
                            Double remainStock = batchSurplusRecordEntity.getRemainingStock();
                            List<BatchFlowBillEntity> billEntityList = sortedInsertMap.get(batchKey);
                            sortedInsertMap.remove(batchKey);
                            for (int i = billEntityList.size() - 1; i >= 0; i--) {
                                BatchFlowBillEntity batchFlowBillEntity = billEntityList.get(i);
                                if (i == billEntityList.size() - 1) {
                                    // 最后一次记录的结存等于当前结存
                                    batchFlowBillEntity.setRemainingCost(remainCost);
                                    batchFlowBillEntity.setRemainingStock(remainStock);
                                } else {
                                    // 前一次的结存等于后一次结存 - 后一次出入库数量和总成本
                                    BatchFlowBillEntity lastStockFlowBillEntity = billEntityList.get(i + 1);
                                    Double num = lastStockFlowBillEntity.getNum();
                                    Double productCost = lastStockFlowBillEntity.getProductCost();
                                    Integer stockType = lastStockFlowBillEntity.getStockType();
                                    if (Objects.equals(stockType,StockTypeEnum.IN_STOCK.getCode()) || Objects.equals(stockType,StockTypeEnum.ORI_STOCK.getCode())) {
                                        remainStock = Arith.sub(remainStock, num);
                                        remainCost = Arith.sub(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType,StockTypeEnum.OUT_STOCK.getCode())) {
                                        remainStock = Arith.add(remainStock, num);
                                        remainCost = Arith.add(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType,StockTypeEnum.ADJUST_STOCK.getCode())) {
                                        remainCost = Arith.sub(remainCost, productCost);
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    }
                                    if (batchFlowBillEntity.getRemainingCost() <= 0.000001 && batchFlowBillEntity.getRemainingCost() > -0.000001) {
                                        batchFlowBillEntity.setRemainingCost(0D);
                                        remainCost = 0D;
                                    }
                                    if (batchFlowBillEntity.getRemainingStock() <= 0.000001 && batchFlowBillEntity.getRemainingStock() > -0.000001) {
                                        batchFlowBillEntity.setRemainingStock(0D);
                                        remainStock = 0D;
                                    }
                                }
                            }
                        }
                        batchSurplusRecordEntity.setRemainingCost(batchSurplusRecordEntity.getRemainingCost() == null ? 0D : batchSurplusRecordEntity.getRemainingCost() - productBatchCostMap.getOrDefault(batchKey, 0D));
                        if (batchSurplusRecordEntity.getRemainingCost() <= 0.000001 && batchSurplusRecordEntity.getRemainingCost() > -0.000001) {
                            batchSurplusRecordEntity.setRemainingCost(0D);
                        }
                        batchSurplusRecordEntity.setRemainingStock(batchSurplusRecordEntity.getRemainingStock() == null ? 0D : batchSurplusRecordEntity.getRemainingStock() - productBatchStockMap.getOrDefault(batchKey, 0D));
                        if (batchSurplusRecordEntity.getRemainingStock() <= 0.000001 && batchSurplusRecordEntity.getRemainingStock() > -0.000001) {
                            batchSurplusRecordEntity.setRemainingStock(0D);
                        }
                    }
                    // 解绑的流水也会生成
                    if (!sortedInsertMap.isEmpty()) {
                        for (List<BatchFlowBillEntity> billEntityList : sortedInsertMap.values()) {
                            Double remainCost = 0D;
                            Double remainStock = 0D;
                            for (int i = billEntityList.size() - 1; i >= 0; i--) {
                                BatchFlowBillEntity batchFlowBillEntity = billEntityList.get(i);
                                if (i == billEntityList.size() - 1) {
                                    // 最后一次记录的结存等于当前结存
                                    batchFlowBillEntity.setRemainingCost(remainCost);
                                    batchFlowBillEntity.setRemainingStock(remainStock);
                                } else {
                                    // 前一次的结存等于后一次结存 - 后一次出入库数量和总成本
                                    BatchFlowBillEntity lastStockFlowBillEntity = billEntityList.get(i + 1);
                                    Double num = lastStockFlowBillEntity.getNum();
                                    Double productCost = lastStockFlowBillEntity.getProductCost();
                                    Integer stockType = lastStockFlowBillEntity.getStockType();
                                    if (Objects.equals(stockType,StockTypeEnum.IN_STOCK.getCode()) || Objects.equals(stockType,StockTypeEnum.ORI_STOCK.getCode())) {
                                        remainStock = Arith.sub(remainStock, num);
                                        remainCost = Arith.sub(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType,StockTypeEnum.OUT_STOCK.getCode())) {
                                        remainStock = Arith.add(remainStock, num);
                                        remainCost = Arith.add(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType, StockTypeEnum.ADJUST_STOCK.getCode())) {
                                        remainCost = Arith.sub(remainCost, productCost);
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    }
                                    if (batchFlowBillEntity.getRemainingCost() <= 0.000001 && batchFlowBillEntity.getRemainingCost() > -0.000001) {
                                        batchFlowBillEntity.setRemainingCost(0D);
                                        remainCost = 0D;
                                    }
                                    if (batchFlowBillEntity.getRemainingStock() <= 0.000001 && batchFlowBillEntity.getRemainingStock() > -0.000001) {
                                        batchFlowBillEntity.setRemainingStock(0D);
                                        remainStock = 0D;
                                    }
                                }
                            }
                        }
                    }
                    if (!batchSurplusRecordEntityList.isEmpty()) {
                        int pageSize = 1000;
                        int totalCount = sortedInsertList.size();
                        int start = 0;
                        while (true) {
                            if ((start + 1) * pageSize >= totalCount) {
                                batchFlowBillModel.insertBatch(sortedInsertList.subList(start * pageSize,totalCount));
                                break;
                            } else {
                                batchFlowBillModel.insertBatch(sortedInsertList.subList(start * pageSize,(start + 1) * pageSize));
                            }
                            start++;
                        }
                        batchSurplusRecordModel.updateBatch(batchSurplusRecordEntityList, corpid);
                    }
                }
            }
            paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
        } catch (Exception e) {
            LOG.error("batchFlowError",e);
            paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
        }
    }

    @Override
    public void insertMonthBeforeBatchFlowNoThread(BaseDTO baseDTO) throws XbbException {
        String corpid = baseDTO.getCorpid();
        batchFlowBillModel.deleteAllByCorpId(corpid);
        batchSurplusRecordModel.deleteAllByCorpId(corpid);
        Long todayInt = DateTimeUtil.getTodayInt();
        Long monthBeforeInt = todayInt - BusinessConstant.THIRTY_ONE_DAY;
        paasRedisHelper.setValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid, new JSONObject(), RedisConstant.TINY_DURATION);
        try {
            // 插入，往前推流水，后面会更新
            batchSurplusRecordModel.insertRecordsFromBatchStock(corpid, monthBeforeInt);
            Long idGte = 0L;
            Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID,corpid);
            param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
            List<WarehouseEntityExt> warehouseEntityExtList = warehouseModel.findEntitys(param);
            if (warehouseEntityExtList.isEmpty()) {
                paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
                return;
            }
            List<Long> warehouseIdList = new ArrayList<>();
            warehouseEntityExtList.forEach(item -> warehouseIdList.add(item.getId()));
            while (true) {
                param.clear();
                param.put(StringConstant.CORPID,corpid);
                param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
                param.put("dataParentIdGte",0L);
                param.put("idGte",idGte);
                param.put("pageSize",1000);
                param.put("orderByStr","id asc");
                List<ProductEntityExt> productEntityExtList = productModel.findEntitys(param);
                if (productEntityExtList.isEmpty()) {
                    break;
                }
                idGte = productEntityExtList.get(productEntityExtList.size()-1).getId();
                List<Long> productIdList = new ArrayList<>();
                productEntityExtList.forEach(item -> productIdList.add(item.getId()));
                List<BatchFlowBillEntity> flowBillEntityList = new ArrayList<>();
                // 入库
                param.clear();
                param.put(ParameterConstant.CORPID,corpid);
                param.put("productIdIn",productIdList);
                param.put("warehouseIdIn",warehouseIdList);
                param.put("batchIsNotEmpty", 1);
                param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
                List<InstockProductEntity> instockProductEntityList = instockProductModel.findEntitys(param);
                LOG.warn(corpid +"/" + instockProductEntityList.size());
                List<Long> idList = new ArrayList<>();
                Map<Long, InstockEntityExt>  instockEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!instockProductEntityList.isEmpty()) {
                    List<Long> instockIdList = new ArrayList<>();
                    instockProductEntityList.forEach(item -> instockIdList.add(item.getIntoWarehouseId()));
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", instockIdList);
                    filterMap.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<InstockEntityExt> instockEntityList = instockModel.findEntitys(filterMap);
                    instockEntityList.forEach(item -> {
                        idList.add(item.getId());
                        instockEntityExtMap.put(item.getId(), item);
                    });
                }
                Map<String,Double>  productBatchStockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<String,Double>  productBatchCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for (InstockProductEntity entity : instockProductEntityList){
                    if (!idList.contains(entity.getIntoWarehouseId())){
                        continue;
                    }
                    // 批次总库存
                    String key = entity.getProductId() + "_" + entity.getWarehouseId()  + "_" + entity.getBatch()  + "_" + entity.getProduceDate()  + "_" + entity.getGuaranteePeriod();
                    Double productBatchStock = productBatchStockMap.getOrDefault(key,0D);
                    productBatchStock = Arith.add(productBatchStock, entity.getProductNum());
                    productBatchStockMap.put(key,productBatchStock);
                    // 批次总成本
                    Double productBatchCost = productBatchCostMap.getOrDefault(key,0D);
                    productBatchCost = Arith.add(productBatchCost, Arith.mul(entity.getProductNum(), entity.getCost()));
                    productBatchCostMap.put(key, productBatchCost);
                    // 流水记录
                    InstockEntityExt instockEntityExt = instockEntityExtMap.get(entity.getIntoWarehouseId());
                    Long dateTime = instockEntityExt.getData().getLong(InstockEnum.TIME.getAttr());
                    Integer type = instockEntityExt.getData().getInteger(InstockEnum.TYPE.getAttr());
                    Integer businessType = InstockTypeEnum.getStockBusinessTypeByCode(type) == 0 ? XbbRefTypeEnum.OTHER_INSTOCK.getCode() : InstockTypeEnum.getStockBusinessTypeByCode(type);
                    Long businessId = null;
                    String businessName = null;
                    if (Objects.equals(businessType, XbbRefTypeEnum.REFUND_INSTOCK.getCode())) {
                        businessId = instockEntityExt.getData().getLong(RefundInstockEnum.CUSTOMER_ID.getAttr());
                        businessName = instockEntityExt.getData().getString(RefundInstockEnum.CUSTOMER_ID_LINKED_TEXT.getAttr());
                    } else if (Objects.equals(businessType, XbbRefTypeEnum.PURCHASE_INSTOCK.getCode())) {
                        businessId = instockEntityExt.getData().getLong(PurchaseInstockEnum.SUPPLIER_ID.getAttr());
                        businessName = instockEntityExt.getData().getString(PurchaseInstockEnum.SUPPLIER_ID_LINKED_TEXT.getAttr());
                    }
                    BatchFlowBillEntity batchFlowBillEntity = new BatchFlowBillEntity(corpid,entity.getParentId(), entity.getProductId(), entity.getWarehouseId(), entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod(), entity.getProduceDate() + entity.getGuaranteePeriod(),
                            businessId, businessName, entity.getProductNum(), entity.getCost() == null ? 0D : entity.getCost(), productBatchStock, productBatchCost, dateTime, businessType, key, entity.getMemo(), entity.getId(), StockTypeEnum.IN_STOCK.getCode()
                            , instockEntityExt.getSerialNo(), entity.getIntoWarehouseId());
                    flowBillEntityList.add(batchFlowBillEntity);
                }
                //出库
                List<OutstockProductEntity> outstockProductEntityList = outstockProductModel.findEntitys(param);
                idList.clear();
                Map<Long, OutstockEntityExt>  outstockEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!outstockProductEntityList.isEmpty()) {
                    List<Long> outstockIdList = new ArrayList<>();
                    outstockProductEntityList.forEach(item -> outstockIdList.add(item.getOutWarehouseId()));
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", outstockIdList);
                    filterMap.put("del", 0);
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    filterMap.put("batchIsNotEmpty", 1);
                    List<OutstockEntityExt> outstockEntityList = outstockModel.findEntitys(filterMap);
                    outstockEntityList.forEach(item -> {
                        idList.add(item.getId());
                        outstockEntityExtMap.put(item.getId(), item);
                    });
                }
                LOG.warn(corpid +"/" + outstockProductEntityList.size());
                for (OutstockProductEntity entity : outstockProductEntityList){
                    if (!idList.contains(entity.getOutWarehouseId())){
                        continue;
                    }
                    String key = entity.getProductId() + "_" + entity.getWarehouseId()  + "_" + entity.getBatch()  + "_" + entity.getProduceDate()  + "_" + entity.getGuaranteePeriod();
                    Double productBatchStock = productBatchStockMap.getOrDefault(key,0D);
                    productBatchStock = Arith.sub(productBatchStock, entity.getProductNum());
                    productBatchStockMap.put(key, productBatchStock);
                    // 分仓总成本
                    Double productBatchCost = productBatchCostMap.getOrDefault(key,0D);
                    productBatchCost = Arith.sub(productBatchCost, Arith.mul(entity.getProductNum(), entity.getCost()));
                    productBatchCostMap.put(key, productBatchCost);
                    // 流水记录
                    OutstockEntityExt outstockEntityExt = outstockEntityExtMap.get(entity.getOutWarehouseId());
                    Long dateTime = outstockEntityExt.getData().getLong(OutstockEnum.TIME.getAttr());
                    Integer type = outstockEntityExt.getData().getInteger(OutstockEnum.TYPE.getAttr());
                    Integer businessType = OutstockTypeEnum.getStockBusinessTypeByCode(type) == 0 ? XbbRefTypeEnum.OTHER_OUTSTOCK.getCode() : OutstockTypeEnum.getStockBusinessTypeByCode(type);
                    Long businessId = null;
                    String businessName = null;
                    if (Objects.equals(businessType, XbbRefTypeEnum.CONTRACT_OUTSTOCK.getCode())) {
                        businessId = outstockEntityExt.getData().getLong(ContractOutstockEnum.LINK_CUSTOMER.getAttr());
                        businessName = outstockEntityExt.getData().getString(ContractOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
                    } else if (Objects.equals(businessType, XbbRefTypeEnum.RETURNED_PURCHASE_OUTSTOCK.getCode())) {
                        businessId = outstockEntityExt.getData().getLong(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER.getAttr());
                        businessName = outstockEntityExt.getData().getString(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER_HIDE.getAttr());
                    } else if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER_OUTSTOCK.getCode())) {
                        businessId = outstockEntityExt.getData().getLong(WorkOrderOutstockEnum.LINK_CUSTOMER.getAttr());
                        businessName = outstockEntityExt.getData().getString(WorkOrderOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
                    }
                    BatchFlowBillEntity batchFlowBillEntity = new BatchFlowBillEntity(corpid, entity.getParentId(), entity.getProductId(), entity.getWarehouseId(), entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod(), entity.getProduceDate() + entity.getGuaranteePeriod(),
                            businessId, businessName, entity.getProductNum(), entity.getCost() == null ? 0D : entity.getCost(), productBatchStock, productBatchCost, dateTime, businessType, key, entity.getMemo(), entity.getId(), StockTypeEnum.OUT_STOCK.getCode()
                            , outstockEntityExt.getSerialNo(), entity.getOutWarehouseId());
                    flowBillEntityList.add(batchFlowBillEntity);
                }
                param.clear();
                param.put(StringConstant.CORPID,corpid);
                param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
                param.put("warehouseIdIn", warehouseIdList);
                param.put("greaterThanAddTime",monthBeforeInt);
                List<WarehouseOristockEntity> warehouseOristockEntityList = warehouseOristockModel.findEntitys(param);
                LOG.warn(corpid +"/" + warehouseOristockEntityList.size());
                Map<Long,WarehouseOristockEntity> oriWarehouseMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                warehouseOristockEntityList.forEach(item -> oriWarehouseMap.put(item.getId().longValue(),item));
                param.put("oriStockIdIn",oriWarehouseMap.keySet());
                param.put("productIdIn",productIdList);
                param.put("batchIsNotEmpty", 1);
                List<WarehouseOristockProductEntity> warehouseOristockProductEntityList = warehouseOristockProductModel.findEntitys(param);
                for (WarehouseOristockProductEntity entity : warehouseOristockProductEntityList) {
                    if (oriWarehouseMap.containsKey(entity.getOristockId().longValue())){
                        WarehouseOristockEntity warehouseOristockEntity = oriWarehouseMap.get(entity.getOristockId().longValue());
                        Long warehouseId = warehouseOristockEntity.getWarehouseId();
                        String key = entity.getProductId() + "_" + warehouseId  + "_" + entity.getBatch()  + "_" + entity.getProduceDate()  + "_" + entity.getGuaranteePeriod();
                        Double productBatchStock = productBatchStockMap.getOrDefault(key,0D);
                        productBatchStock = Arith.add(productBatchStock, entity.getOristockNum());
                        productBatchStockMap.put(key, productBatchStock);
                        // 批次总成本
                        Double productBatchCost = productBatchCostMap.getOrDefault(key,0D);
                        productBatchCost = Arith.add(productBatchCost, Arith.mul(entity.getOristockNum(), entity.getCost() == null ? 0D : entity.getCost()));
                        productBatchCostMap.put(key, productBatchCost);
                        // 流水记录
                        Long dateTime = DateTimeUtil.getTodayInt(warehouseOristockEntity.getAddTime());
                        BatchFlowBillEntity batchFlowBillEntity = new BatchFlowBillEntity(corpid, entity.getParentId(), entity.getProductId(), warehouseId, entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod(), entity.getProduceDate() + entity.getGuaranteePeriod(),
                                null, null, entity.getOristockNum(), entity.getCost() == null ? 0D : entity.getCost(), productBatchStock, productBatchCost, dateTime, XbbRefTypeEnum.ORI_STOCK.getCode(), key, entity.getMemo(), entity.getId(), StockTypeEnum.ORI_STOCK.getCode()
                                , warehouseOristockEntity.getSerialNo(), entity.getOristockId().longValue());
                        flowBillEntityList.add(batchFlowBillEntity);
                    }
                }
                List<BatchFlowBillEntity> sortedInsertList = flowBillEntityList.stream().sorted(Comparator.comparing(BatchFlowBillEntity::getDate)).collect(Collectors.toList());
                Map<String, List<BatchFlowBillEntity>> sortedInsertMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                sortedInsertList.forEach(item -> {
                    List<BatchFlowBillEntity> sortedList = sortedInsertMap.getOrDefault(item.getBatchKey(), new ArrayList<>());
                    sortedList.add(item);
                    sortedInsertMap.put(item.getBatchKey(), sortedList);
                });
                if (!productBatchStockMap.keySet().isEmpty()) {
                    param.clear();
                    param.put(StringConstant.CORPID,corpid);
                    param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
                    param.put("date",monthBeforeInt);
                    param.put("batchKeyIn", productBatchStockMap.keySet());
                    List<BatchSurplusRecordEntity> batchSurplusRecordEntityList = batchSurplusRecordModel.findEntitys(param);
                    for (BatchSurplusRecordEntity batchSurplusRecordEntity : batchSurplusRecordEntityList) {
                        String batchKey = batchSurplusRecordEntity.getBatchKey();
                        if (sortedInsertMap.containsKey(batchKey)) {
                            Double remainCost = batchSurplusRecordEntity.getRemainingCost();
                            Double remainStock = batchSurplusRecordEntity.getRemainingStock();
                            List<BatchFlowBillEntity> billEntityList = sortedInsertMap.get(batchKey);
                            sortedInsertMap.remove(batchKey);
                            for (int i = billEntityList.size() - 1; i >= 0; i--) {
                                BatchFlowBillEntity batchFlowBillEntity = billEntityList.get(i);
                                if (i == billEntityList.size() - 1) {
                                    // 最后一次记录的结存等于当前结存
                                    batchFlowBillEntity.setRemainingCost(remainCost);
                                    batchFlowBillEntity.setRemainingStock(remainStock);
                                } else {
                                    // 前一次的结存等于后一次结存 - 后一次出入库数量和总成本
                                    BatchFlowBillEntity lastStockFlowBillEntity = billEntityList.get(i + 1);
                                    Double num = lastStockFlowBillEntity.getNum();
                                    Double productCost = lastStockFlowBillEntity.getProductCost();
                                    Integer stockType = lastStockFlowBillEntity.getStockType();
                                    if (stockType == 1 || stockType == 3) {
                                        remainStock = Arith.sub(remainStock, num);
                                        remainCost = Arith.sub(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (stockType == 2) {
                                        remainStock = Arith.add(remainStock, num);
                                        remainCost = Arith.add(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    }
                                    if (batchFlowBillEntity.getRemainingCost() <= 0.000001 && batchFlowBillEntity.getRemainingCost() > -0.000001) {
                                        batchFlowBillEntity.setRemainingCost(0D);
                                        remainCost = 0D;
                                    }
                                    if (batchFlowBillEntity.getRemainingStock() <= 0.000001 && batchFlowBillEntity.getRemainingStock() > -0.000001) {
                                        batchFlowBillEntity.setRemainingStock(0D);
                                        remainStock = 0D;
                                    }
                                }
                            }
                        }
                        batchSurplusRecordEntity.setRemainingCost(batchSurplusRecordEntity.getRemainingCost() == null ? 0D : batchSurplusRecordEntity.getRemainingCost() - productBatchCostMap.getOrDefault(batchKey, 0D));
                        if (batchSurplusRecordEntity.getRemainingCost() <= 0.000001 && batchSurplusRecordEntity.getRemainingCost() > -0.000001) {
                            batchSurplusRecordEntity.setRemainingCost(0D);
                        }
                        batchSurplusRecordEntity.setRemainingStock(batchSurplusRecordEntity.getRemainingCost() == null ? 0D : batchSurplusRecordEntity.getRemainingCost() - productBatchStockMap.getOrDefault(batchKey, 0D));
                        if (batchSurplusRecordEntity.getRemainingStock() <= 0.000001 && batchSurplusRecordEntity.getRemainingStock() > -0.000001) {
                            batchSurplusRecordEntity.setRemainingStock(0D);
                        }
                    }
                    // 解绑的流水也会生成
                    if (!sortedInsertMap.isEmpty()) {
                        for (List<BatchFlowBillEntity> billEntityList : sortedInsertMap.values()) {
                            Double remainCost = 0D;
                            Double remainStock = 0D;
                            for (int i = billEntityList.size() - 1; i >= 0; i--) {
                                BatchFlowBillEntity batchFlowBillEntity = billEntityList.get(i);
                                if (i == billEntityList.size() - 1) {
                                    // 最后一次记录的结存等于当前结存
                                    batchFlowBillEntity.setRemainingCost(remainCost);
                                    batchFlowBillEntity.setRemainingStock(remainStock);
                                } else {
                                    // 前一次的结存等于后一次结存 - 后一次出入库数量和总成本
                                    BatchFlowBillEntity lastStockFlowBillEntity = billEntityList.get(i + 1);
                                    Double num = lastStockFlowBillEntity.getNum();
                                    Double productCost = lastStockFlowBillEntity.getProductCost();
                                    Integer stockType = lastStockFlowBillEntity.getStockType();
                                    if (stockType == 1 || stockType == 3) {
                                        remainStock = Arith.sub(remainStock, num);
                                        remainCost = Arith.sub(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (stockType == 2) {
                                        remainStock = Arith.add(remainStock, num);
                                        remainCost = Arith.add(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    }
                                    if (batchFlowBillEntity.getRemainingCost() <= 0.000001 && batchFlowBillEntity.getRemainingCost() > -0.000001) {
                                        batchFlowBillEntity.setRemainingCost(0D);
                                        remainCost = 0D;
                                    }
                                    if (batchFlowBillEntity.getRemainingStock() <= 0.000001 && batchFlowBillEntity.getRemainingStock() > -0.000001) {
                                        batchFlowBillEntity.setRemainingStock(0D);
                                        remainStock = 0D;
                                    }
                                }
                            }
                        }
                    }
                    if (!batchSurplusRecordEntityList.isEmpty()) {
                        int pageSize = 1000;
                        int totalCount = sortedInsertList.size();
                        LOG.warn(corpid +"/" + totalCount);
                        int start = 0;
                        while (true) {
                            if ((start + 1) * pageSize >= totalCount) {
                                batchFlowBillModel.insertBatch(sortedInsertList.subList(start * pageSize,totalCount));
                                break;
                            } else {
                                batchFlowBillModel.insertBatch(sortedInsertList.subList(start * pageSize,(start + 1) * pageSize));
                            }
                            start++;
                        }
                        LOG.warn(corpid +"/" + batchSurplusRecordEntityList.size());
                        batchSurplusRecordModel.updateBatch(batchSurplusRecordEntityList, corpid);
                    }
                }
            }
            paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
        } catch (Exception e) {
            LOG.error("batchFlowError",e);
            paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
        }
    }

    @Override
    public void dataConsistencyUpdateBatchFlowBill(String corpid, Long dataId, Integer visible) {
        try {
            batchFlowBillModel.updateBatchByParentId(corpid, dataId, visible);
        } catch (Exception e) {
            LOG.error("BatchFlowBillServiceImpl.dataConsistencyUpdateBatchFlowBill fail， corpid=" + corpid + " dataId=" + dataId + "  visible=" + visible, e);
            e.printStackTrace();
        }
    }


    @Override
    public void insertBeforeBatchFlow(FlowBillInsertDTO flowBillInsertDTO) throws XbbException{
        Long beforeSeconds = flowBillInsertDTO.getBeforeSeconds();
        if (beforeSeconds == null) {
            beforeSeconds = BusinessConstant.THIRTY_ONE_DAY;
            flowBillInsertDTO.setBeforeSeconds(BusinessConstant.THIRTY_ONE_DAY);
        }
        String corpid = flowBillInsertDTO.getCorpid();
        Long todayInt = DateTimeUtil.getTodayInt();
        Long monthBeforeInt = todayInt - beforeSeconds;
        try {
            Long idGte = 0L;
            Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID,corpid);
            param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
            List<WarehouseEntityExt> warehouseEntityExtList = warehouseModel.findEntitys(param);
            if (warehouseEntityExtList.isEmpty()) {
                paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
                return;
            }
            List<Long> warehouseIdList = new ArrayList<>();
            warehouseEntityExtList.forEach(item -> warehouseIdList.add(item.getId()));
            while (true) {
                param.clear();
                param.put(StringConstant.CORPID,corpid);
                param.put("columns", new StringBuffer(" id "));
                param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
                param.put("dataParentIdGte",0L);
                param.put("idGte",idGte);
                param.put("pageSize",1000);
                param.put("orderByStr","id asc");
                List<ProductEntityExt> productEntityExtList = productModel.findEntitysWithoutSub(param);
                if (productEntityExtList.isEmpty()) {
                    break;
                }
                idGte = productEntityExtList.get(productEntityExtList.size()-1).getId();
                List<Long> productIdList = new ArrayList<>();
                productEntityExtList.forEach(item -> productIdList.add(item.getId()));
                List<BatchFlowBillEntity> flowBillEntityList = new ArrayList<>();
                // 入库
                param.clear();
                param.put(ParameterConstant.CORPID,corpid);
                param.put("productIdIn",productIdList);
                param.put("warehouseIdIn",warehouseIdList);
                param.put("batchIsNotEmpty", 1);
                param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
                List<InstockProductEntity> instockProductEntityList = instockProductModel.findEntitys(param);
                List<Long> idList = new ArrayList<>();
                Map<Long, InstockEntityExt>  instockEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!instockProductEntityList.isEmpty()) {
                    List<Long> instockIdList = new ArrayList<>();
                    instockProductEntityList.forEach(item -> instockIdList.add(item.getIntoWarehouseId()));
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", instockIdList);
                    filterMap.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<InstockEntityExt> instockEntityList = instockModel.findEntitys(filterMap);
                    instockEntityList.forEach(item -> {
                        idList.add(item.getId());
                        instockEntityExtMap.put(item.getId(), item);
                    });
                }
                //去除工作流中数据
                removeAddData(idList, corpid);
                Map<String,Double>  productBatchStockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<String,Double>  productBatchCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for (InstockProductEntity entity : instockProductEntityList){
                    if (!idList.contains(entity.getIntoWarehouseId())){
                        continue;
                    }
                    // 批次总库存
                    String key = entity.getProductId() + "_" + entity.getWarehouseId()  + "_" + entity.getBatch()  + "_" + entity.getProduceDate()  + "_" + entity.getGuaranteePeriod();
                    Double productBatchStock = productBatchStockMap.getOrDefault(key,0D);
                    productBatchStock = Arith.add(productBatchStock, entity.getProductNum());
                    productBatchStockMap.put(key,productBatchStock);
                    // 批次总成本
                    Double productBatchCost = productBatchCostMap.getOrDefault(key,0D);
                    productBatchCost = Arith.add(productBatchCost, Arith.mul(entity.getProductNum(), entity.getCost()));
                    productBatchCostMap.put(key, productBatchCost);
                    // 流水记录
                    InstockEntityExt instockEntityExt = instockEntityExtMap.get(entity.getIntoWarehouseId());
                    Long dateTime = instockEntityExt.getData().getLong(InstockEnum.TIME.getAttr());
                    Integer type = instockEntityExt.getData().getInteger(InstockEnum.TYPE.getAttr());
                    Integer businessType = InstockTypeEnum.getStockBusinessTypeByCode(type) == 0 ? XbbRefTypeEnum.OTHER_INSTOCK.getCode() : InstockTypeEnum.getStockBusinessTypeByCode(type);
                    Long businessId = null;
                    String businessName = null;
                    if (Objects.equals(businessType, XbbRefTypeEnum.REFUND_INSTOCK.getCode())) {
                        businessId = instockEntityExt.getData().getLong(RefundInstockEnum.CUSTOMER_ID.getAttr());
                        businessName = instockEntityExt.getData().getString(RefundInstockEnum.CUSTOMER_ID_LINKED_TEXT.getAttr());
                    } else if (Objects.equals(businessType, XbbRefTypeEnum.PURCHASE_INSTOCK.getCode())) {
                        businessId = instockEntityExt.getData().getLong(PurchaseInstockEnum.SUPPLIER_ID.getAttr());
                        businessName = instockEntityExt.getData().getString(PurchaseInstockEnum.SUPPLIER_ID_LINKED_TEXT.getAttr());
                    }
                    businessName = StringUtil.filterEmoji(businessName);
                    BatchFlowBillEntity batchFlowBillEntity = new BatchFlowBillEntity(corpid, entity.getParentId(), entity.getProductId(), entity.getWarehouseId(), entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod(), entity.getProduceDate() + entity.getGuaranteePeriod(),
                            businessId, businessName, entity.getProductNum(), entity.getCost() == null ? 0D : entity.getCost(), productBatchStock, productBatchCost, dateTime, businessType, key, entity.getMemo(), entity.getId(), StockTypeEnum.IN_STOCK.getCode()
                            , instockEntityExt.getSerialNo(), entity.getIntoWarehouseId(), entity.getAddTime());
                    flowBillEntityList.add(batchFlowBillEntity);
                }
                //出库
                List<OutstockProductEntity> outstockProductEntityList = outstockProductModel.findEntitys(param);
                idList.clear();
                Map<Long, OutstockEntityExt>  outstockEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!outstockProductEntityList.isEmpty()) {
                    List<Long> outstockIdList = new ArrayList<>();
                    outstockProductEntityList.forEach(item -> outstockIdList.add(item.getOutWarehouseId()));
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", outstockIdList);
                    filterMap.put("del", 0);
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<OutstockEntityExt> outstockEntityList = outstockModel.findEntitys(filterMap);
                    outstockEntityList.forEach(item -> {
                        idList.add(item.getId());
                        outstockEntityExtMap.put(item.getId(), item);
                    });
                }
                //去除工作流中数据
                removeAddData(idList, corpid);
                for (OutstockProductEntity entity : outstockProductEntityList){
                    if (!idList.contains(entity.getOutWarehouseId())){
                        continue;
                    }
                    String key = entity.getProductId() + "_" + entity.getWarehouseId()  + "_" + entity.getBatch()  + "_" + entity.getProduceDate()  + "_" + entity.getGuaranteePeriod();
                    Double productBatchStock = productBatchStockMap.getOrDefault(key,0D);
                    productBatchStock = Arith.sub(productBatchStock, entity.getProductNum());
                    productBatchStockMap.put(key, productBatchStock);
                    // 分仓总成本
                    Double productBatchCost = productBatchCostMap.getOrDefault(key,0D);
                    productBatchCost = Arith.sub(productBatchCost, Arith.mul(entity.getProductNum(), entity.getCost()));
                    productBatchCostMap.put(key, productBatchCost);
                    // 流水记录
                    OutstockEntityExt outstockEntityExt = outstockEntityExtMap.get(entity.getOutWarehouseId());
                    Long dateTime = outstockEntityExt.getData().getLong(OutstockEnum.TIME.getAttr());
                    Integer type = outstockEntityExt.getData().getInteger(OutstockEnum.TYPE.getAttr());
                    Integer businessType = OutstockTypeEnum.getStockBusinessTypeByCode(type) == 0 ? XbbRefTypeEnum.OTHER_OUTSTOCK.getCode() : OutstockTypeEnum.getStockBusinessTypeByCode(type);
                    Long businessId = null;
                    String businessName = null;
                    if (Objects.equals(businessType, XbbRefTypeEnum.CONTRACT_OUTSTOCK.getCode())) {
                        businessId = outstockEntityExt.getData().getLong(ContractOutstockEnum.LINK_CUSTOMER.getAttr());
                        businessName = outstockEntityExt.getData().getString(ContractOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
                    } else if (Objects.equals(businessType, XbbRefTypeEnum.RETURNED_PURCHASE_OUTSTOCK.getCode())) {
                        businessId = outstockEntityExt.getData().getLong(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER.getAttr());
                        businessName = outstockEntityExt.getData().getString(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER_HIDE.getAttr());
                    } else if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER_OUTSTOCK.getCode())) {
                        businessId = outstockEntityExt.getData().getLong(WorkOrderOutstockEnum.LINK_CUSTOMER.getAttr());
                        businessName = outstockEntityExt.getData().getString(WorkOrderOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
                    }
                    businessName = StringUtil.filterEmoji(businessName);
                    BatchFlowBillEntity batchFlowBillEntity = new BatchFlowBillEntity(corpid, entity.getParentId(), entity.getProductId(), entity.getWarehouseId(), entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod(), entity.getProduceDate() + entity.getGuaranteePeriod(),
                            businessId, businessName, entity.getProductNum(), entity.getCost() == null ? 0D : entity.getCost(), productBatchStock, productBatchCost, dateTime, businessType, key, entity.getMemo(), entity.getId(), StockTypeEnum.OUT_STOCK.getCode()
                            , outstockEntityExt.getSerialNo(), entity.getOutWarehouseId(), entity.getAddTime());
                    flowBillEntityList.add(batchFlowBillEntity);
                }
                //成本调整单
                //获取成本调整单产品关联表数据
                List<CostAdjustProductEntity> costAdjustProductEntityList = costAdjustProductModel.findEntitys(param);
                idList.clear();
                Map<Long, CostAdjustEntityExt> costAdjustEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!costAdjustProductEntityList.isEmpty()) {
                    List<Long> costAdjustIdList = new ArrayList<>();
                    costAdjustProductEntityList.forEach(item -> costAdjustIdList.add(item.getCostAdjustId()));
                    //获取成本调整单集合
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", costAdjustIdList);
                    filterMap.put("del", 0);
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<CostAdjustEntityExt> costAdjustEntityExtList = costAdjustModel.findEntitys(filterMap);
                    costAdjustEntityExtList.forEach(item -> {
                        idList.add(item.getId());
                        costAdjustEntityExtMap.put(item.getId(), item);
                    });
                    for (CostAdjustProductEntity entity : costAdjustProductEntityList){
                        if (!idList.contains(entity.getCostAdjustId())){
                            continue;
                        }
                        String key = entity.getProductId() + "_" + entity.getWarehouseId()  + "_" + entity.getBatch()  + "_" + entity.getProduceDate()  + "_" + entity.getGuaranteePeriod();
                        Double productBatchStock = productBatchStockMap.getOrDefault(key,0D);
                        productBatchStockMap.put(key, productBatchStock);
                        // 分仓总成本
                        Double productBatchCost = productBatchCostMap.getOrDefault(key,0D);
                        productBatchCost = Arith.add(productBatchCost, entity.getCostAdjustAmount().doubleValue());
                        productBatchCostMap.put(key, productBatchCost);
                        // 流水记录
                        CostAdjustEntityExt costAdjustEntityExt = costAdjustEntityExtMap.get(entity.getCostAdjustId());
                        Long dateTime = costAdjustEntityExt.getData().getLong(CostAdjustEnum.ADJUST_TIME.getAttr());
                        Integer businessType = XbbRefTypeEnum.COST_ADJUST.getCode();
                        BatchFlowBillEntity batchFlowBillEntity = new BatchFlowBillEntity(corpid, entity.getParentId(), entity.getProductId(), entity.getWarehouseId(), entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod(), entity.getProduceDate() + entity.getGuaranteePeriod(),
                                null, null, entity.getProductNum() == null ? 0 : entity.getProductNum(), entity.getCostAdjustAmount().doubleValue(), productBatchStock, productBatchCost, dateTime, businessType, key, entity.getMemo(), entity.getId(), StockTypeEnum.ADJUST_STOCK.getCode()
                                , costAdjustEntityExt.getSerialNo(), entity.getCostAdjustId(),Long.valueOf(entity.getAddTime()));
                        flowBillEntityList.add(batchFlowBillEntity);
                    }
                }
                //期初
                param.clear();
                param.put(StringConstant.CORPID,corpid);
                param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
                param.put("warehouseIdIn", warehouseIdList);
                param.put("greaterThanAddTime",monthBeforeInt);
                param.put("batchIsNotEmpty", 1);
                List<WarehouseOristockEntity> warehouseOristockEntityList = warehouseOristockModel.findEntitys(param);
                Map<Long,WarehouseOristockEntity> oriWarehouseMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                warehouseOristockEntityList.forEach(item -> oriWarehouseMap.put(item.getId().longValue(),item));
                param.put("oriStockIdIn",oriWarehouseMap.keySet());
                param.put("productIdIn",productIdList);
                param.put("batchIsNotEmpty", 1);
                List<WarehouseOristockProductEntity> warehouseOristockProductEntityList = warehouseOristockProductModel.findEntitys(param);
                for (WarehouseOristockProductEntity entity : warehouseOristockProductEntityList) {
                    if (oriWarehouseMap.containsKey(entity.getOristockId().longValue())){
                        WarehouseOristockEntity warehouseOristockEntity = oriWarehouseMap.get(entity.getOristockId().longValue());
                        Long warehouseId = warehouseOristockEntity.getWarehouseId();
                        String key = entity.getProductId() + "_" + warehouseId  + "_" + entity.getBatch()  + "_" + entity.getProduceDate()  + "_" + entity.getGuaranteePeriod();
                        Double productBatchStock = productBatchStockMap.getOrDefault(key,0D);
                        productBatchStock = Arith.add(productBatchStock, entity.getOristockNum());
                        productBatchStockMap.put(key, productBatchStock);
                        // 批次总成本
                        Double productBatchCost = productBatchCostMap.getOrDefault(key,0D);
                        productBatchCost = Arith.add(productBatchCost, Arith.mul(entity.getOristockNum(), entity.getCost() == null ? 0D : entity.getCost()));
                        productBatchCostMap.put(key, productBatchCost);
                        // 流水记录
                        Long dateTime = DateTimeUtil.getTodayInt(warehouseOristockEntity.getAddTime());
                        BatchFlowBillEntity batchFlowBillEntity = new BatchFlowBillEntity(corpid, entity.getParentId(), entity.getProductId(), warehouseId, entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod(), entity.getProduceDate() + entity.getGuaranteePeriod(),
                                null, null, entity.getOristockNum(), entity.getCost() == null ? 0D : entity.getCost(), productBatchStock, productBatchCost, dateTime, XbbRefTypeEnum.ORI_STOCK.getCode(), key, entity.getMemo(), entity.getId(), StockTypeEnum.ORI_STOCK.getCode()
                                , warehouseOristockEntity.getSerialNo(), entity.getOristockId().longValue(), entity.getAddTime());
                        flowBillEntityList.add(batchFlowBillEntity);
                    }
                }
                List<BatchFlowBillEntity> sortedInsertList = flowBillEntityList.stream().sorted(Comparator.comparing(BatchFlowBillEntity::getDate)).collect(Collectors.toList());
                Map<String, List<BatchFlowBillEntity>> sortedInsertMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                sortedInsertList.forEach(item -> {
                    List<BatchFlowBillEntity> sortedList = sortedInsertMap.getOrDefault(item.getBatchKey(), new ArrayList<>());
                    sortedList.add(item);
                    sortedInsertMap.put(item.getBatchKey(), sortedList);
                });
                if (!productBatchStockMap.keySet().isEmpty()) {
                    param.clear();
                    param.put(StringConstant.CORPID,corpid);
                    param.put(StringConstant.DEL,DelEnum.NORMAL.getDel());
                    param.put("date",monthBeforeInt);
                    param.put("batchKeyIn", productBatchStockMap.keySet());
                    List<BatchSurplusRecordEntity> batchSurplusRecordEntityList = batchSurplusRecordModel.findEntitys(param);
                    for (BatchSurplusRecordEntity batchSurplusRecordEntity : batchSurplusRecordEntityList) {
                        String batchKey = batchSurplusRecordEntity.getBatchKey();
                        if (sortedInsertMap.containsKey(batchKey)) {
                            Double remainCost = batchSurplusRecordEntity.getRemainingCost();
                            Double remainStock = batchSurplusRecordEntity.getRemainingStock();
                            List<BatchFlowBillEntity> billEntityList = sortedInsertMap.get(batchKey);
                            sortedInsertMap.remove(batchKey);
                            for (int i = billEntityList.size() - 1; i >= 0; i--) {
                                BatchFlowBillEntity batchFlowBillEntity = billEntityList.get(i);
                                if (i == billEntityList.size() - 1) {
                                    // 最后一次记录的结存等于当前结存
                                    batchFlowBillEntity.setRemainingCost(remainCost);
                                    batchFlowBillEntity.setRemainingStock(remainStock);
                                } else {
                                    // 前一次的结存等于后一次结存 - 后一次出入库数量和总成本
                                    BatchFlowBillEntity lastStockFlowBillEntity = billEntityList.get(i + 1);
                                    Double num = lastStockFlowBillEntity.getNum();
                                    Double productCost = lastStockFlowBillEntity.getProductCost();
                                    Integer stockType = lastStockFlowBillEntity.getStockType();
                                    if (Objects.equals(stockType,StockTypeEnum.IN_STOCK.getCode()) || Objects.equals(stockType,StockTypeEnum.ORI_STOCK.getCode())) {
                                        remainStock = Arith.sub(remainStock, num);
                                        remainCost = Arith.sub(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType,StockTypeEnum.OUT_STOCK.getCode())) {
                                        remainStock = Arith.add(remainStock, num);
                                        remainCost = Arith.add(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType,StockTypeEnum.ADJUST_STOCK.getCode())) {
                                        remainCost = Arith.sub(remainCost, productCost);
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    }
                                    if (batchFlowBillEntity.getRemainingCost() <= 0.000001 && batchFlowBillEntity.getRemainingCost() > -0.000001) {
                                        batchFlowBillEntity.setRemainingCost(0D);
                                        remainCost = 0D;
                                    }
                                    if (batchFlowBillEntity.getRemainingStock() <= 0.000001 && batchFlowBillEntity.getRemainingStock() > -0.000001) {
                                        batchFlowBillEntity.setRemainingStock(0D);
                                        remainStock = 0D;
                                    }
                                }
                            }
                        }
                        batchSurplusRecordEntity.setRemainingCost(batchSurplusRecordEntity.getRemainingCost() == null ? 0D : batchSurplusRecordEntity.getRemainingCost() - productBatchCostMap.getOrDefault(batchKey, 0D));
                        if (batchSurplusRecordEntity.getRemainingCost() <= 0.000001 && batchSurplusRecordEntity.getRemainingCost() > -0.000001) {
                            batchSurplusRecordEntity.setRemainingCost(0D);
                        }
                        batchSurplusRecordEntity.setRemainingStock(batchSurplusRecordEntity.getRemainingStock() == null ? 0D : batchSurplusRecordEntity.getRemainingStock() - productBatchStockMap.getOrDefault(batchKey, 0D));
                        if (batchSurplusRecordEntity.getRemainingStock() <= 0.000001 && batchSurplusRecordEntity.getRemainingStock() > -0.000001) {
                            batchSurplusRecordEntity.setRemainingStock(0D);
                        }
                    }
                    // 解绑的流水也会生成
                    if (!sortedInsertMap.isEmpty()) {
                        for (List<BatchFlowBillEntity> billEntityList : sortedInsertMap.values()) {
                            Double remainCost = 0D;
                            Double remainStock = 0D;
                            for (int i = billEntityList.size() - 1; i >= 0; i--) {
                                BatchFlowBillEntity batchFlowBillEntity = billEntityList.get(i);
                                if (i == billEntityList.size() - 1) {
                                    // 最后一次记录的结存等于当前结存
                                    batchFlowBillEntity.setRemainingCost(remainCost);
                                    batchFlowBillEntity.setRemainingStock(remainStock);
                                } else {
                                    // 前一次的结存等于后一次结存 - 后一次出入库数量和总成本
                                    BatchFlowBillEntity lastStockFlowBillEntity = billEntityList.get(i + 1);
                                    Double num = lastStockFlowBillEntity.getNum();
                                    Double productCost = lastStockFlowBillEntity.getProductCost();
                                    Integer stockType = lastStockFlowBillEntity.getStockType();
                                    if (Objects.equals(stockType,StockTypeEnum.IN_STOCK.getCode()) || Objects.equals(stockType,StockTypeEnum.ORI_STOCK.getCode())) {
                                        remainStock = Arith.sub(remainStock, num);
                                        remainCost = Arith.sub(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType,StockTypeEnum.OUT_STOCK.getCode())) {
                                        remainStock = Arith.add(remainStock, num);
                                        remainCost = Arith.add(remainCost, Arith.mul(num, productCost));
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType, StockTypeEnum.ADJUST_STOCK.getCode())) {
                                        remainCost = Arith.sub(remainCost, productCost);
                                        batchFlowBillEntity.setRemainingCost(remainCost);
                                        batchFlowBillEntity.setRemainingStock(remainStock);
                                    }
                                    if (batchFlowBillEntity.getRemainingCost() <= 0.000001 && batchFlowBillEntity.getRemainingCost() > -0.000001) {
                                        batchFlowBillEntity.setRemainingCost(0D);
                                        remainCost = 0D;
                                    }
                                    if (batchFlowBillEntity.getRemainingStock() <= 0.000001 && batchFlowBillEntity.getRemainingStock() > -0.000001) {
                                        batchFlowBillEntity.setRemainingStock(0D);
                                        remainStock = 0D;
                                    }
                                }
                            }
                        }
                    }
                    if (!batchSurplusRecordEntityList.isEmpty()) {
                        int pageSize = 1000;
                        int totalCount = sortedInsertList.size();
                        int start = 0;
                        while (true) {
                            if ((start + 1) * pageSize >= totalCount) {
                                batchFlowBillModel.insertBatch(sortedInsertList.subList(start * pageSize,totalCount));
                                break;
                            } else {
                                batchFlowBillModel.insertBatch(sortedInsertList.subList(start * pageSize,(start + 1) * pageSize));
                            }
                            start++;
                        }
                        batchSurplusRecordModel.updateBatch(batchSurplusRecordEntityList, corpid);
                    }
                }
            }
        } catch (Exception e) {
            LOG.error("batchFlowError",e);
            LOG.error("公司" + corpid + "失败了" + e);
            paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
        }
    }

    /**
     * 去掉新建进流程的数据
     * @param idList
     * @param corpid
     */
    private void removeAddData(List<Long> idList, String corpid) {
        if (CollectionsUtil.isNotEmpty(idList)) {
            if (!commonHelp.isOpenWorkFlow(corpid)) {
                // 检测是否开通了工作流
                return;
            }
            // 查询审批中的任务
            Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            filterMap.put(StringConstant.CORPID, corpid);
            filterMap.put("dataIdIn", idList);
            filterMap.put("del", 0);
            filterMap.put("flowStatusIn", FlowStatusEnum.inApproval());
            filterMap.put("columns", " id, data_id, flow_status");
            List<WorkflowTaskEntity> taskList = workflowTaskModel.findEntities(filterMap);
            if (CollectionsUtil.isNotEmpty(taskList)) {
                List<Long> taskIdList = new ArrayList<>();
                taskList.forEach(item->{
                    taskIdList.add(item.getId());
                });
                filterMap.clear();
                filterMap.put(StringConstant.CORPID, corpid);
                filterMap.put("taskIdIn", taskIdList);
                filterMap.put("del", 0);
                List<WorkflowTransferDataEntity> list = workflowTransferDataModel.findEntities(filterMap);
                if (CollectionsUtil.isNotEmpty(list)) {
                    WorkflowTransferPOJO workflowTransferPOJO;
                    for (WorkflowTransferDataEntity item : list) {
                        workflowTransferPOJO = JSON.parseObject(item.getData().toJSONString(), WorkflowTransferPOJO.class);
                        Integer operate = workflowTransferPOJO.getOperate();
                        if (Objects.equals(WorkflowOperateEnum.ADD.getType(), operate)) {
//                            移除掉新建的入库单或者出库单
                            // 工作流的新建时，入库产品表和出库产品表都会生成；但是库存并未增加，所以需要移除掉新建进工作流的入库单和出库单
                            idList.remove(workflowTransferPOJO.getData().getId());
                        }
                    }
                }
            }
        }
    }
}
